/**
 * @brief 使用epoll在一个线程中同时处理多个client
 * 
 * @note 使用EPOLLET的时候，应该使用while read + 非阻塞，让errno返回EAGAIN为止
 *       因为不能确定一次就把数据全读完了，而EPOLLET模式不会再次产生EPOLLIN
 *       默认的POLLLT则会一直产生EPOLLIN，直到数据全部读完
 */

#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/epoll.h>

int main(void)
{
    int server_sockfd, client_sockfd;
    int server_len, client_len;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;

    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

    server_address.sin_family = AF_INET;
    //注意：inet_addr返回的已经是按照网络字节序存储的网络地址了，所以不能再加htonl了    
    server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
    server_address.sin_port = htons(11021);
    server_len = sizeof(server_address);
    bind(server_sockfd, (struct sockaddr*)&server_address, server_len);

    struct epoll_event ev, events[20];
    int epfd = epoll_create(256);

    ev.data.fd = server_sockfd;
    ev.events = EPOLLIN;
    epoll_ctl(epfd, EPOLL_CTL_ADD, server_sockfd, &ev);//注冊epoll事件

    listen(server_sockfd, 5);
    printf("server waiting\n");

    client_len = sizeof(client_address);

    while (1)
    {
        int nfds = epoll_wait(epfd, events, 20, -1);
        for (int i = 0; i < nfds; i++)
        {
            if (events[i].data.fd == server_sockfd)
            {
                client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_address, &client_len);
                printf("new client accepted, sock_fd = %d\n", client_sockfd);
                ev.data.fd = client_sockfd;
                //ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
                ev.events = EPOLLIN;
                //ev.events = EPOLLIN | EPOLLET;
                epoll_ctl(epfd, EPOLL_CTL_ADD, client_sockfd, &ev);//新增套接字
            }
            else if (events[i].events & EPOLLIN)
            {
                int sock_fd = events[i].data.fd;
                if(sock_fd < 0)
                {
                    continue;
                }

                char buf[1024];
                int n = read(sock_fd, buf, 3);
                if (n == 0)
                {
                    close(sock_fd);
                    printf("client %d exit\n", sock_fd);
                    
                    ev.data.fd = sock_fd;
                    epoll_ctl(epfd, EPOLL_CTL_DEL, sock_fd, &ev);
                }
                else
                {
                    buf[n] = '\0';
                    printf("read from client %d, len %d, content: %s\n", sock_fd, n, buf);
                }
            }
            else if (events[i].events & EPOLLOUT)
            {
                int sock_fd = events[i].data.fd;
                printf("client %d can write\n", sock_fd);
            }
        }
    }

    return 0;
}
